package com.bestcem.xm.award.service.impl;

import cn.hutool.core.lang.Validator;
import com.bestcem.bp.grpc.v1.base.Code;
import com.bestcem.bp.pay.grpc.v1.services.ActivityRemain;
import com.bestcem.bp.pay.grpc.v1.services.GetActivityRemainRequest;
import com.bestcem.bp.pay.grpc.v1.services.GetActivityRemainResponse;
import com.bestcem.xm.award.constant.Constants;
import com.bestcem.xm.award.constant.RedisKeyConstant;
import com.bestcem.xm.award.dao.AwardHistoryDao;
import com.bestcem.xm.award.dao.AwardRequestDao;
import com.bestcem.xm.award.dao.DrawRecordDao;
import com.bestcem.xm.award.entity.mongo.BaseEntity;
import com.bestcem.xm.award.entity.pojo.AwardAwardDO;
import com.bestcem.xm.award.entity.pojo.AwardRequestDO;
import com.bestcem.xm.award.enums.AwardRequestStatusEnum;
import com.bestcem.xm.award.enums.SurveyStatusEnum;
import com.bestcem.xm.award.grpc.client.ActivityGrpcService;
import com.bestcem.xm.award.grpc.client.AwardSurveyGrpcService;
import com.bestcem.xm.award.service.dto.requestaward.RequestAwardDTO;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.componet.redis.service.RedisService;
import com.bestcem.xm.survey.grpc.v1.services.GetSurveyScoreBySeqRequest;
import com.bestcem.xm.survey.grpc.v1.services.GetSurveyScoreBySeqResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

import static com.bestcem.xm.award.enums.AwardRequestStatusEnum.*;
import static com.bestcem.xm.award.enums.RedPacketStatusEnum.SENDING;
import static com.bestcem.xm.award.enums.RedPacketStatusEnum.SENT;
import static com.bestcem.xm.award.enums.SurveyStatusEnum.FINISH;

/**
 * 奖励校验
 *
 * @author jie.yao <jie.yao@idiaoyan.com>
 * @version v1.0
 * @date 2021/7/2 15:39
 */
@Component
public class AwardCheck {

    @Resource
    private AwardRequestDao awardRequestDao;

    @Resource(name = "recordDaoMysql")
    private DrawRecordDao drawRecordDao;

    @Resource
    private AwardHistoryDao awardHistoryDao;

    @Resource
    private ActivityGrpcService activityGrpcService;

    @Resource
    private AwardSurveyGrpcService awardSurveyGrpcService;
    
    @Resource
    private RedisService redisService;

    /**
     * 校验答题时间
     *
     * @param requestAwardDTO 校验参数
     * @return 校验结果 状态、信息
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/6 11:20
     */
    public AwardRequestStatusEnum getCheckCostTimeResult(RequestAwardDTO requestAwardDTO) {
        Optional<RequestAwardDTO> optional = Optional.ofNullable(requestAwardDTO);
        // 规则设置答题时间
        Integer ruleTime = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getRule).map(AwardAwardDO.Rule::getTime).orElse(null);
        // 答卷状态
        Integer surveyStatus = optional.map(RequestAwardDTO::getAwardRequestDO)
                .map(AwardRequestDO::getSurvey).map(AwardRequestDO.Survey::getStatus).orElse(Constants.MINUS_ONE);
        // 答题时间
        Integer costTime = optional.map(RequestAwardDTO::getAwardRequestDO)
                .map(AwardRequestDO::getSurvey).map(AwardRequestDO.Survey::getCostTime).orElse(Constants.ZERO);
        if (Validator.isNotNull(ruleTime)) {
            if (!this.checkStatus(surveyStatus)) {
                return SURVEY_STATUS;
            }
            return this.checkCostTime(costTime, ruleTime) ? PASS : TIME;
        }
        return PASS;
    }

    /**
     * 校验账号领取次数
     *
     * @param requestAwardDTO 校验参数
     * @return 校验结果 状态、信息
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/6 11:20
     */
    public AwardRequestStatusEnum getCheckAccountResult(RequestAwardDTO requestAwardDTO) {
        Optional<RequestAwardDTO> optional = Optional.ofNullable(requestAwardDTO);
        // 规则设置账号领取次数
        Integer ruleAccount = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getRule).map(AwardAwardDO.Rule::getAccount).orElse(null);
        // 会员id
        String memberId = optional.map(RequestAwardDTO::getDeliver)
                .map(AwardRequestDO.Deliver::getMemberId).orElse(Constants.BLANK);
        // 投放id
        String deliverId = optional.map(RequestAwardDTO::getDeliver)
                .map(AwardRequestDO.Deliver::getDeliverId).orElse(Constants.BLANK);
        // 奖励id
        String awardId = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(BaseEntity::getId).orElse(Constants.BLANK);
        if (Validator.isNotNull(ruleAccount) && StringUtils.isNotBlank(memberId)) {
            return this.checkAccount(deliverId, awardId, memberId, ruleAccount) ? PASS : ACCOUNT;
        }
        return PASS;
    }

    /**
     * 校验ip次数限制
     * 目前设计下，如果设置了ip次数限制，不管设置为多少，均为1次
     *
     * @param requestAwardDTO 校验参数
     * @return 校验结果 状态、信息
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/6 11:20
     */
    public AwardRequestStatusEnum getCheckIpResult(RequestAwardDTO requestAwardDTO) {
        Optional<RequestAwardDTO> optional = Optional.ofNullable(requestAwardDTO);
        // 规则设置ip次数
        Integer ruleIp = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getRule).map(AwardAwardDO.Rule::getIp).orElse(null);
        // ip
        String remoteIp = optional.map(RequestAwardDTO::getRemoteIp).orElse(Constants.BLANK);
        // 投放id
        String deliverId = optional.map(RequestAwardDTO::getDeliver)
                .map(AwardRequestDO.Deliver::getDeliverId).orElse(Constants.BLANK);
        // 奖励id
        String awardId = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(BaseEntity::getId).orElse(Constants.BLANK);
        if (StringUtils.isBlank(remoteIp) || StringUtils.isBlank(deliverId) || StringUtils.isBlank(awardId)) {
            return IP;
        }
        if (Validator.isNotNull(ruleIp) && StringUtils.isNotBlank(remoteIp)) {
            return this.checkIp(deliverId, awardId, remoteIp) ? PASS : IP;
        }
        return PASS;
    }

    /**
     * 校验设备访问次数
     * 目前设计下，如果设置了device次数限制，不管设置为多少，均为1次
     *
     * @param requestAwardDTO 校验参数
     * @return 校验结果 状态、信息
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/6 11:20
     */
    public AwardRequestStatusEnum getCheckDeviceResult(RequestAwardDTO requestAwardDTO) {
        Optional<RequestAwardDTO> optional = Optional.ofNullable(requestAwardDTO);
        // 规则设置设备访问次数
        Integer ruleDevice = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getRule).map(AwardAwardDO.Rule::getDevice).orElse(null);
        // 访问次数
        Integer vt = optional.map(RequestAwardDTO::getVt).orElse(Constants.ZERO);
        if (Validator.isNotNull(ruleDevice)) {
            return this.checkDevice(vt) ? PASS : DEVICE;
        }
        return PASS;
    }

    /**
     * 校验奖励抽奖次数
     *
     * @param requestAwardDTO 校验参数
     * @return 校验结果 状态、信息
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/6 11:20
     */
    public AwardRequestStatusEnum getCheckTotalResult(RequestAwardDTO requestAwardDTO) {
        Optional<RequestAwardDTO> optional = Optional.ofNullable(requestAwardDTO);
        // 设置的抽奖次数上限
        long ruleTotal = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getBasic).map(AwardAwardDO.Basic::getTotalCount).orElse(Constants.ZERO);
        // 投放id
        String deliverId = optional.map(RequestAwardDTO::getDeliver)
                .map(AwardRequestDO.Deliver::getDeliverId).orElse(Constants.BLANK);
        // 奖励id
        String awardId = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(BaseEntity::getId).orElse(Constants.BLANK);
        if (StringUtils.isBlank(deliverId) || StringUtils.isBlank(awardId)) {
            return TOTAL;
        }
        if (ruleTotal > Constants.ZERO) {
            return this.checkTotal(awardId, deliverId, ruleTotal) ? PASS : TOTAL;
        }
        return PASS;
    }

    /**
     * 校验答卷分数
     *
     * @param requestAwardDTO 校验参数
     * @return 校验结果 状态、信息
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/6 11:20
     */
    public AwardRequestStatusEnum getCheckSurveyScoreResult(RequestAwardDTO requestAwardDTO) {
        Optional<RequestAwardDTO> optional = Optional.ofNullable(requestAwardDTO);
        // 规则设置答卷分数
        Integer ruleScore = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getRule).map(AwardAwardDO.Rule::getSscore).orElse(null);
        // 答卷状态
        Integer surveyStatus = optional.map(RequestAwardDTO::getAwardRequestDO)
                .map(AwardRequestDO::getSurvey).map(AwardRequestDO.Survey::getStatus).orElse(Constants.MINUS_ONE);
        // 答卷唯一标识
        Long surveySeq = optional.map(RequestAwardDTO::getAwardRequestDO)
                .map(AwardRequestDO::getSurvey).map(AwardRequestDO.Survey::getSeq).orElse(null);
        // 问卷项目id
        String projectId = optional.map(RequestAwardDTO::getAwardAwardDO).map(AwardAwardDO::getProjectId).orElse(null);
        if (Validator.isNotNull(ruleScore)) {
            if (!this.checkStatus(surveyStatus)) {
                return SURVEY_STATUS;
            }
            return this.checkScore(ruleScore, surveySeq, projectId) ? PASS : SURVEY_SCORE;
        }
        return PASS;
    }

    /**
     * 校验当前时间是否在奖励开始时间与结束时间期间
     *
     * @param requestAwardDTO 校验参数
     * @return 校验结果 状态、信息
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/8 16:19
     */
    public AwardRequestStatusEnum checkInProceeding(RequestAwardDTO requestAwardDTO) {
        Optional<RequestAwardDTO> optional = Optional.ofNullable(requestAwardDTO);
        // 开始时间
        Date startTime = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getBasic).map(AwardAwardDO.Basic::getStartTime).orElse(null);
        // 结束时间
        Date stopTime = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getBasic).map(AwardAwardDO.Basic::getStopTime).orElse(null);
        return this.checkInProceeding(startTime, stopTime) ? PASS : INACTIVE;
    }

    /**
     * 校验奖励微信红包领取次数
     *
     * @param requestAwardDTO 校验参数
     * @return 校验结果 状态、信息
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/8 16:19
     */
    public AwardRequestStatusEnum getCheckProbResult(RequestAwardDTO requestAwardDTO) {
        Optional<RequestAwardDTO> optional = Optional.ofNullable(requestAwardDTO);
        // 红包概率
        long prob = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getBasic).map(AwardAwardDO.Basic::getProbability).orElse(Constants.LONG_HUNDRED);
        // 钱包活动id
        String activityId = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getActivityId).orElse(Constants.BLANK);
        // 奖励id
        String awardId = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(BaseEntity::getId).orElse(Constants.BLANK);
        if (prob == Constants.ZERO) {
            if (StringUtils.isBlank(activityId)) {
                return TOTAL;
            }
            return checkEmpty(activityId) ? PASS : TOTAL;
        } else {
            return checkReachable(awardId) ? PASS : TOTAL;
        }
    }

    /**
     * 校验是否达到当日领取上限
     *
     * @param requestAwardDTO 校验参数
     * @return 校验结果 状态、信息
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    public AwardRequestStatusEnum getCheckDayLimitResult(RequestAwardDTO requestAwardDTO) {
        Optional<RequestAwardDTO> optional = Optional.ofNullable(requestAwardDTO);
        // 规则设置当日领取上限
        Integer ruleDay = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getRule).map(AwardAwardDO.Rule::getDay).orElse(null);
        // 奖励id
        String awardId = optional.map(RequestAwardDTO::getAwardAwardDO)
                .map(BaseEntity::getId).orElse(Constants.BLANK);
        if (Validator.isNotNull(ruleDay) || StringUtils.isBlank(awardId)) {
            return this.checkDayLimit(awardId, ruleDay) ? PASS : TODAY;
        }
        return PASS;
    }

    /**
     * 校验答卷状态
     *
     * @param surveyStatus 答卷状态 {@link SurveyStatusEnum}
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    private boolean checkStatus(Integer surveyStatus) {
        return surveyStatus == FINISH.getIndex();
    }

    /**
     * 校验答题时间
     *
     * @param costTime 答题花费时间
     * @param ruleTime 规则设置答题时间
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    private boolean checkCostTime(Integer costTime, Integer ruleTime) {
        return costTime >= ruleTime;
    }

    /**
     * 校验账号领取次数
     *
     * @param deliverId   投放id
     * @param awardId     奖励id
     * @param memberId    会员id
     * @param ruleAccount 规则设置账号领取次数
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    private boolean checkAccount(String deliverId, String awardId, String memberId, Integer ruleAccount) {
        AwardRequestDO awardRequestDO = this.buildAwardRequest(deliverId, awardId, memberId, null);
        long count = awardRequestDao.countAwardRequestByAwardRequest(awardRequestDO);
        return count < ruleAccount;
    }

    /**
     * 校验ip次数限制
     * 目前设计下，如果设置了ip次数限制，不管设置为多少，均为1次
     *
     * @param deliverId 投放id
     * @param awardId   奖励id
     * @param remoteIp  ip
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    private boolean checkIp(String deliverId, String awardId, String remoteIp) {
        AwardRequestDO awardRequestDO = this.buildAwardRequest(deliverId, awardId, null, remoteIp);
        long count = awardRequestDao.countAwardRequestByAwardRequest(awardRequestDO);
        return count < Constants.ONE;
    }

    /**
     * 校验设备访问次数
     * 目前设计下，如果设置了device次数限制，不管设置为多少，均为1次
     *
     * @param vt 设备访问次数
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    private boolean checkDevice(Integer vt) {
        return vt < Constants.ONE;
    }

    /**
     * 校验奖励抽奖次数
     *
     * @param awardId   奖励id
     * @param deliverId 投放id (一个奖励可以被多个投放使用,因此此参数无效)
     * @param ruleTotal 规则设置抽奖次数上限
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    private boolean checkTotal(String awardId, String deliverId, long ruleTotal) {
        // 抽奖次数
        long count = drawRecordDao.getAwardAlreadyDrawTotal(awardId);
        return count < ruleTotal;
    }

    /**
     * 校验答卷分数
     *
     * @param ruleScore 规则设置答卷分数
     * @param surveySeq 答卷标识
     * @param projectId 问卷项目id
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    private boolean checkScore(Integer ruleScore, Long surveySeq, String projectId) {
        // 添加校验答卷标识校验
        if (surveySeq == null) {
            return false;
        }
        // 分数规则设置为null，直接通过
        if (ruleScore == null) {
            return true;
        }
        // 根据答卷标识获取答卷分数
        GetSurveyScoreBySeqRequest.Builder builder = GetSurveyScoreBySeqRequest.newBuilder();
        builder.setSeq(surveySeq.intValue()).setProjectId(projectId);
        GetSurveyScoreBySeqResponse surveyScoreBySeqResponse = awardSurveyGrpcService.getSurveyScoreBySeq(builder.build());
        if (surveyScoreBySeqResponse.getStatus().getCodeValue() != Code.OK_VALUE) {
            return false;
        }
        return new BigDecimal(surveyScoreBySeqResponse.getScore()).compareTo(new BigDecimal(ruleScore)) >= 0;
    }

    /**
     * 校验当前时间是否在奖励开始时间与结束时间期间
     *
     * @param startTime 开始时间
     * @param stopTime  结束时间
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    private boolean checkInProceeding(Date startTime, Date stopTime) {
        if (Validator.isNull(startTime) || Validator.isNull(stopTime)) {
            return false;
        }
        Date mongoNow = DateUtil.getDate();
        return mongoNow.compareTo(startTime) >= 0 && mongoNow.compareTo(stopTime) <= 0;
    }

    /**
     * 100%中奖， 检查红包是否领取完毕
     *
     * @param activityId 钱包活动id
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/8 16:19
     */
    private boolean checkEmpty(String activityId) {
        GetActivityRemainRequest request = GetActivityRemainRequest.newBuilder().setId(activityId).build();
        GetActivityRemainResponse activityRemain = activityGrpcService.getActivityRemain(request);
        if (activityRemain.getStatus().getCodeValue() != Code.OK_VALUE) {
            return false;
        }
        Optional<GetActivityRemainResponse> optional = Optional.of(activityRemain);
        // 剩余总金额
        Integer amount = optional.map(GetActivityRemainResponse::getRemain)
                .map(ActivityRemain::getAmount).orElse(Constants.ZERO);
        // 剩余红包个数
        Integer num = optional.map(GetActivityRemainResponse::getRemain)
                .map(ActivityRemain::getNum).orElse(Constants.ZERO);
        return amount > Constants.ZERO && num > Constants.ZERO;
    }

    /**
     * 概率中奖，检查参与人数是否到达预计人数
     *
     * @param awardId 奖励id
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/8 16:19
     */
    public boolean checkReachable(String awardId) {
        return redisService.lLen(String.format(RedisKeyConstant.AWARD_POOL, awardId)) > Constants.ZERO;
    }

    /**
     * 校验是否达到当日领取上限
     *
     * @param awardId 奖励id
     * @param ruleDay 规则设置当日领取上限
     * @return 校验结果 true:通过 false:未通过
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    private boolean checkDayLimit(String awardId, Integer ruleDay) {
        List<Integer> status = new ArrayList<>();
        status.add(SENT.getIndex());
        status.add(SENDING.getIndex());
        long count = awardHistoryDao.countByIdAndStatus(awardId, status);
        return count < ruleDay;
    }

    /**
     * 构造查询条件
     *
     * @param deliverId 投放id
     * @param awardId   奖励id
     * @param memberId  会员id
     * @param remoteIp  ip
     * @return AwardRequest查询条件
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/16 13:42
     */
    private AwardRequestDO buildAwardRequest(String deliverId, String awardId, String memberId, String remoteIp) {
        AwardRequestDO awardRequestDO = new AwardRequestDO();
        awardRequestDO.setAwardId(awardId);
        awardRequestDO.setStatus(PASS.getIndex());
        if (StringUtils.isNotBlank(remoteIp)) {
            awardRequestDO.setIp(remoteIp);
        }
        AwardRequestDO.Deliver deliver = new AwardRequestDO.Deliver();
        deliver.setDeliverId(deliverId);
        if (StringUtils.isNotBlank(memberId)) {
            deliver.setMemberId(memberId);
        }
        awardRequestDO.setDeliverDTO(deliver);
        return awardRequestDO;
    }

    /**
     * 按顺序返回校验结果
     *
     * @param checkResults 多个校验结果
     * @return 校验结果
     * @author jie.yao <jie.yao@idiaoyan.com>
     * @date 2021/7/6 11:20
     */
    public AwardRequestStatusEnum checkAll(AwardRequestStatusEnum... checkResults) {
        for (AwardRequestStatusEnum requestStatusEnum : checkResults) {
            if (requestStatusEnum.getIndex() != PASS.getIndex()) {
                return requestStatusEnum;
            }
        }
        return PASS;
    }
}
